//
//  ZJLabel.m
//  TextKitDemo
//
//  Created by ZJ on 2018/7/16.
//  Copyright © 2018年 iCatch Technology Inc. All rights reserved.
//

#import "ZJLabel.h"

@interface ZJLabel ()

@property (nonatomic, strong) NSTextStorage *textStorage;
@property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer;

@property (nonatomic, strong) NSArray *patterns;
@property (nonatomic, strong) NSMutableArray *linkRanges;
@property (nonatomic, assign) NSRange selectedRange;

@end

@implementation ZJLabel

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

/**
 正则表达式匹配字符串
 */
- (NSArray *)patterns {
    if (_patterns == nil) {
        _patterns = @[@"[a-zA-Z]*://[a-zA-Z0-9/\\.]*", @"#.*?#", @"@[\\u4e00-\\u9fa5a-zA-Z0-9_-]*"];
    }
    
    return _patterns;
}

// MARK: - init functions
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    if (self) {
        [self prepareLabel];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    
    if (self) {
        [self prepareLabel];
    }
    
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    _textContainer.size = self.bounds.size;
}

// MARK: - set link text properties
- (void)setLinkTextColor:(UIColor *)linkTextColor {
    self.linkTextColor = linkTextColor ? linkTextColor : [UIColor blueColor];
}

- (void)setSelectedBackgroudColor:(UIColor *)selectedBackgroudColor {
    self.selectedBackgroudColor = selectedBackgroudColor ? selectedBackgroudColor : [UIColor lightGrayColor];
}

// MARK: - override properties
- (void)setText:(NSString *)text {
    [super setText:text];
    
    [self updateTextStorage];
}

- (void)setAttributedText:(NSAttributedString *)attributedText {
    [super setAttributedText:attributedText];
    
    [self updateTextStorage];
}

- (void)setFont:(UIFont *)font {
    [super setFont:font];
    
    [self updateTextStorage];
}

- (void)setTextColor:(UIColor *)textColor {
    [super setTextColor:textColor];
    
    [self updateTextStorage];
}

// MARK: - update text storage and redraw text
- (void)updateTextStorage {
    if (self.attributedText != nil) {
        
    } else if (self.text != nil) {
        self.attributedText = [[NSAttributedString alloc] initWithString:self.text];
    } else {
        return;
    }
    
    NSMutableAttributedString *attrStringM = [self addLineBreak:self.attributedText];
    [self regexLinkRanges:attrStringM];
    [self addLinkAttribute:attrStringM];
    
    [self.textStorage setAttributedString:attrStringM];
    
    [self setNeedsDisplay];
}

/// add link attribute
- (void)addLinkAttribute:(NSMutableAttributedString *)attrStringM {
    if (attrStringM.length == 0) {
        return;
    }
    
    NSRange range = NSMakeRange(0, 0);
    // old
    NSDictionary *attributes = [attrStringM attributesAtIndex:0 effectiveRange:&range];;
    // now
    NSMutableDictionary *attributesM = [[NSMutableDictionary alloc] initWithDictionary:attributes];
    
    attributesM[NSFontAttributeName] = self.font;
    attributesM[NSForegroundColorAttributeName] = self.textColor;
    
    [attrStringM addAttributes:attributesM range:range];
    
    attributesM[NSForegroundColorAttributeName] = self.linkTextColor ? self.linkTextColor : [UIColor blueColor];
    attributesM[NSUnderlineStyleAttributeName] = [[NSNumber alloc] initWithUnsignedInteger:NSUnderlineStyleSingle];
    attributesM[NSUnderlineColorAttributeName] = self.linkTextColor ? self.linkTextColor : [UIColor blueColor];
    attributesM[NSLigatureAttributeName] = [[NSNumber alloc] initWithInt:0];
    attributesM[NSKernAttributeName] = [[NSNumber alloc] initWithInt:2];
    attributesM[NSStrokeColorAttributeName] = [UIColor greenColor];
    
    for (NSString *r in self.linkRanges) {
        [attrStringM setAttributes:attributesM range:NSRangeFromString(r)];
    }
}

/// use regex check all link range
- (void)regexLinkRanges:(NSAttributedString *)attrString {
    NSRange regexRange = NSMakeRange(0, attrString.length);
    
    for (NSString *pattern in self.patterns) {
        NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators error:nil];
        NSArray *results = [regex matchesInString:attrString.string options:NSMatchingReportProgress range:regexRange];
        
        for (NSTextCheckingResult *r in results) {
            NSRange range = [r rangeAtIndex:0];
            [self.linkRanges addObject:NSStringFromRange(range)];
        }
    }
}

/// add line break mode
- (NSMutableAttributedString *)addLineBreak:(NSAttributedString *)attrString {
    NSMutableAttributedString *attrStringM = [[NSMutableAttributedString alloc] initWithAttributedString:attrString];
    
    if (attrStringM.length == 0) {
        return attrStringM;
    }
    
    NSRange range = NSMakeRange(0, 0);
    NSDictionary *attributes = [attrStringM attributesAtIndex:0 effectiveRange:&range];
//    NSParagraphStyle *paragraphStyle = attributes[NSParagraphStyleAttributeName];
    
    NSMutableDictionary *attributesM = [[NSMutableDictionary alloc] initWithDictionary:attributes];
    NSMutableParagraphStyle *paragraphStyleM = [[NSMutableParagraphStyle alloc] init];

    /*
     NSLineBreakByCharWrapping;以字符为显示单位显示，后面部分省略不显示。
     NSLineBreakByClipping;剪切与文本宽度相同的内容长度，后半部分被删除。
     NSLineBreakByTruncatingHead;前面部分文字以……方式省略，显示尾部文字内容。
     NSLineBreakByTruncatingMiddle;中间的内容以……方式省略，显示头尾的文字内容。
     NSLineBreakByTruncatingTail;结尾部分的内容以……方式省略，显示头的文字内容。
     NSLineBreakByWordWrapping;以单词为显示单位显示，后面部分省略不显示。
     */

    /*
    if (paragraphStyle != nil) {
        [paragraphStyleM setParagraphStyle:paragraphStyle];
        paragraphStyleM.lineBreakMode = NSLineBreakByWordWrapping;
    } else {
        paragraphStyleM.lineBreakMode = NSLineBreakByWordWrapping;
    }
     */
    
    paragraphStyleM.lineBreakMode = NSLineBreakByTruncatingTail;
    attributesM[NSParagraphStyleAttributeName] = paragraphStyleM;
    [attrStringM setAttributes:attributesM range:range];

    return attrStringM;
}

/// draw text
- (void)drawTextInRect:(CGRect)rect {
    NSRange range = NSMakeRange(0, self.textStorage.length);
    
    [self.layoutManager drawBackgroundForGlyphRange:range atPoint:CGPointZero];
    [self.layoutManager drawGlyphsForGlyphRange:range atPoint:CGPointZero];
}

// MARK: - touch events
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CGPoint location = [[touches anyObject] locationInView:self];
    
    _selectedRange = [self linkRangeAtLocation:location];
    [self modifySelectedAttribute:YES];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CGPoint location = [touches.anyObject locationInView:self];
    NSRange range = [self linkRangeAtLocation:location];
    
    if (!(range.length == _selectedRange.location && range.length == _selectedRange.length)) {
        [self modifySelectedAttribute:NO];
        _selectedRange = range;
        [self modifySelectedAttribute:YES];
    } else {
        [self modifySelectedAttribute:NO];
    }
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if (!(_selectedRange.location == 0 && _selectedRange.length == 0)) {
        NSString *text = [self.textStorage.string substringWithRange:_selectedRange];
        
        if ([self.delegate respondsToSelector:@selector(labelDidSelectedLinkText:text:)]) {
            [self.delegate labelDidSelectedLinkText:self text:text];
        }
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self modifySelectedAttribute:NO];
        });
    }
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self modifySelectedAttribute:NO];
}

/// modify selected attribute
- (void)modifySelectedAttribute:(BOOL)isSelect {
    if (_selectedRange.location == 0 && _selectedRange.length == 0) {
        return;
    }
    
    NSMutableDictionary *attributesM = [NSMutableDictionary new];
    attributesM[NSForegroundColorAttributeName] = self.linkTextColor ? self.linkTextColor : [UIColor blueColor];
    NSRange range = _selectedRange;
    
    if (isSelect) {
        attributesM[NSBackgroundColorAttributeName] = self.selectedBackgroudColor ? self.selectedBackgroudColor : [UIColor lightGrayColor];
    } else {
        attributesM[NSBackgroundColorAttributeName] = [UIColor clearColor];
        _selectedRange = NSMakeRange(0, 0);
    }
    
    [self.textStorage addAttributes:attributesM range:range];
    
    [self setNeedsDisplay];
}

/// get link range
- (NSRange)linkRangeAtLocation:(CGPoint)location {
    if (self.textStorage.length == 0) {
        return NSMakeRange(0, 0);
    }
    
    NSUInteger index = [self.layoutManager glyphIndexForPoint:location inTextContainer:self.textContainer];
    
    for (NSString *r in self.linkRanges) {
        NSRange range = NSRangeFromString(r);
        
        if (index >= range.location && index <= range.location + range.length) {
            return range;
        }
    }
    
    return NSMakeRange(0, 0);
}

// MARK: - private functions
- (void)prepareLabel {
    [self.textStorage addLayoutManager:self.layoutManager];
    [self.layoutManager addTextContainer:self.textContainer];
    
    self.textContainer.lineFragmentPadding = 0;
    
    [self setUserInteractionEnabled:YES];
}

- (NSTextStorage *)textStorage {
    if (_textStorage == nil) {
        _textStorage = [[NSTextStorage alloc] init];
    }
    
    return _textStorage;
}

- (NSLayoutManager *)layoutManager {
    if (_layoutManager == nil) {
        _layoutManager = [[NSLayoutManager alloc] init];
    }
    
    return _layoutManager;
}

- (NSTextContainer *)textContainer {
    if (_textContainer == nil) {
        _textContainer = [[NSTextContainer alloc] init];
    }
    
    return _textContainer;
}

- (NSMutableArray *)linkRanges {
    if (_linkRanges == nil) {
        _linkRanges = [NSMutableArray new];
    }
    
    return _linkRanges;
}

@end
